using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.Json;
using CSharpKit.Extensions;
using CSharpKit.Native.ECCODES;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace csk.ntv.grib
{
    [TestClass]
    public class UnitTest_GRIB_windy
    {
        /// <summary>
        /// 键迭代
        /// </summary>
        [TestMethod]
        public void windy()
        {
            Assert.IsFalse(false);

            var status = 0;
            var fname = "d:/sfxData/ecmwf/20200802.grib";
            var fin = eccodes.codes_fopen(fname, FileOpenModel.Read);

            // List<List<(string name, string value)>>>
            var myDictionary_header = new Dictionary<string, List<(string name, string value)>>();
            var myDictionary_values = new Dictionary<string, double[]>();

            // skip read only and computed keys (跳过只读和计算键)
            // unsigned long key_iterator_filter_flags = CODES_KEYS_ITERATOR_SKIP_READ_ONLY |
            //                                           CODES_KEYS_ITERATOR_SKIP_COMPUTED;
            KeyIteratorFilter key_iterator_filter_flags = KeyIteratorFilter.CODES_KEYS_ITERATOR_ALL_KEYS
                                        | KeyIteratorFilter.CODES_KEYS_ITERATOR_SKIP_DUPLICATES
                                        ;

            // choose a namespace.
            // name_space = NULL to get all the keys
            // e.g. "all" "ls", "time", "parameter", "geography", "statistics"
            string name_space = "all";
            // string name_space = "ls";
            // string name_space = "time";
            // string name_space = "parameter";
            // string name_space = "geography";
            // string name_space = "statistics";

            var vlen = 1024;
            var value = new byte[vlen];

            IntPtr h = IntPtr.Zero;
            int msg_count = 0;
            while ((h = eccodes.codes_handle_new_from_file(IntPtr.Zero, fin, ProductKind.GRIB, out int err)) != IntPtr.Zero)
            {
                // 消息
                var msgno = string.Format("GRIB N. {0:D03}", ++msg_count);

                // 测试
                if (msg_count > 4)
                {
                    eccodes.codes_handle_delete(h);
                    break;
                }

                Debug.WriteLine(string.Format("-- {0} --\n", msgno));


                // 生成键迭代器句柄
                IntPtr kiter = eccodes.codes_keys_iterator_new(h, key_iterator_filter_flags, name_space);
                if (kiter == IntPtr.Zero) break;
                Assert.IsTrue(kiter != IntPtr.Zero);

                // 保存 header
                var key_name_values = new List<(string name, string value)>();
                myDictionary_header[msgno] = key_name_values;

                // 迭代搜索键名和键值
                while (eccodes.codes_keys_iterator_next(kiter))
                {
                    var knv = get_key_name_value(kiter);
                    key_name_values.Add(knv);

                    if (knv.name == "shortName")
                    {
                        Debug.WriteLine(string.Format("{0}: {1} = {2}\n", msgno, knv.name, knv.value));
                    }
                }

                // 释放键迭代器句柄
                eccodes.codes_keys_iterator_delete(kiter);

                // get values
                // 怎么知道是 double 型？
                status = eccodes.codes_get_size(h, "values", out uint values_len);
                var values = new double[values_len * Marshal.SizeOf<double>()];
                status = eccodes.codes_get_double_array(h, "values", values, ref values_len);

                // 保存 values
                myDictionary_values[msgno] = values;

                eccodes.codes_handle_delete(h);
            }

            // 关闭文件句柄
            var stats = eccodes.codes_fclose(fin);

            output_text("d:/temp/dict.txt", myDictionary_header);
            output_json("d:/temp/dict.json", myDictionary_header, myDictionary_values);

            Assert.IsFalse(false);
        }

        private (string name, string value) get_key_name_value(IntPtr kiter)
        {
            var key_name = eccodes.codes_keys_iterator_get_name(kiter);
            var key_value = eccodes.codes_keys_iterator_get_string(kiter);
            return (key_name, key_value);
        }

        private void output_text(string path, Dictionary<string, List<(string name, string value)>> headers)
        {
            using (var fs = File.Open(path, FileMode.Create))
            {
                using (var sw = new StreamWriter(fs))
                {
                    var msg_count = 0;
                    foreach (var item in headers)
                    {
                        var msgno = string.Format("GRIB N. {0:D03}", ++msg_count);
                        var knvs = headers[msgno];
                        sw.WriteLine("==========================================================================================");
                        sw.WriteLine(msgno);
                        sw.WriteLine("==========================================================================================");

                        knvs.ForEach(p =>
                        {
                            sw.WriteLine(string.Format("{0} = {1}", p.name, p.value));
                        });
                    }
                }
            }
        }

        private void output_json(string path, Dictionary<string, List<(string name, string value)>> headers, Dictionary<string, double[]> values)
        {
            // 
            var grib_msg_ids = new List<string>();
            var grib_msg_headers = new List<List<(string name, string value)>>();

            var msg_count = 0;
            foreach (var item in headers)
            {
                var key = string.Format("GRIB N. {0:D03}", ++msg_count);
                var knv_index = headers[key].FindIndex(p =>
                {
                    var ret_u1 = p.name == "paramId" & p.value == "165";
                    var ret_u2 = p.name == "shortName" & p.value == "10u";

                    var ret_v1 = p.name == "paramId" & p.value == "166";
                    var ret_v2 = p.name == "shortName" & p.value == "10v";

                    return ret_u1 || ret_v1;
                });

                if (knv_index >= 0)
                {
                    grib_msg_ids.Add(key);
                    grib_msg_headers.Add(headers[key]);
                }

                // 有uv 就可以 (测试用)
                if (grib_msg_ids.Count == 2)
                {
                    break;
                }


            }

            // 
            var windy_models = new List<WindModel>();

            for (var i = 0; i < grib_msg_ids.Count; i++)
            {
                var key = grib_msg_ids[i];
                var wdata = values[key];

                // name-value 链表转换成字典
                var name_value_list = grib_msg_headers[i];
                var name_value_dict = new Dictionary<string, string>();
                name_value_list.ForEach(p => { name_value_dict[p.name] = p.value; });

                var wmodel = new WindModel()
                {
                    header = new WindHeader(),
                    data = wdata,
                    // data = new double[4] { 1, 2, 3, 4 },
                };

                // GRIB 1
                if (name_value_dict["editionNumber"] == "1")
                {
                    // 参数类别
                    wmodel.header.parameterCategory = 2;    // no key

                    var paramId = name_value_dict["paramId"].ToInt();
                    var shortName = name_value_dict["shortName"].ToString();

                    // 参数编码
                    wmodel.header.parameterNumber = paramId == 165 ? 2 : (paramId == 166) ? 3 : 0;  // u or v
                    wmodel.header.parameterNumberName = paramId == 165 ? "U wind component" : (paramId == 166) ? "V wind component" : "Unknown";

                    // 格点数量
                    wmodel.header.nx = name_value_dict["Ni"].ToInt();
                    wmodel.header.ny = name_value_dict["Nj"].ToInt();

                    // 格点间距
                    wmodel.header.dx = name_value_dict["iDirectionIncrementInDegrees"].ToFloat();
                    wmodel.header.dy = name_value_dict["jDirectionIncrementInDegrees"].ToFloat();

                    // lon
                    wmodel.header.lo1 = name_value_dict["longitudeOfFirstGridPointInDegrees"].ToFloat();
                    wmodel.header.lo2 = name_value_dict["longitudeOfLastGridPointInDegrees"].ToFloat();

                    // lat
                    wmodel.header.la1 = name_value_dict["latitudeOfFirstGridPointInDegrees"].ToFloat();
                    wmodel.header.la2 = name_value_dict["latitudeOfLastGridPointInDegrees"].ToFloat();

                    // misc
                    wmodel.header.parameterUnit = name_value_dict["units"].ToString();
                    wmodel.header.refTime = "2017-02-01 23:00:00";
                }

                // GRIB 2
                if (name_value_dict["editionNumber"] == "2")
                {
                    // 
                }

                windy_models.Add(wmodel);
            }

            Assert.IsTrue(true);

            // 
            to_windy_json(path, windy_models);

            return;
        }

        void to_windy_json(string path, List<WindModel> windy_models)
        {
            Assert.IsTrue(true);
            // 
            using (var fs = File.Open(path, FileMode.Create))
            {
                using (var sw = new StreamWriter(fs))
                {
                    var wm = windy_models.ToArray();
                    var json = JsonSerializer.Serialize(wm);
                    sw.WriteLine(json);
                    // sw.WriteLineAsync(json);
                }
            }

            Assert.IsTrue(true);
        }



        // {{END}}
    }



}


